package com.example.myapplication.ui.viewmodel

import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import com.example.myapplication.App
import com.example.myapplication.DEFAULT_CHUNK_SIZE
import com.example.myapplication.databaseThreadPool
import com.example.myapplication.exceptions.FileUploadTaskByteErrorException
import com.example.myapplication.exceptions.FileUploadTaskException
import com.example.myapplication.exceptions.FileUploadTaskNotExistException
import com.example.myapplication.exceptions.FileUploadTaskNotFoundException
import com.example.myapplication.extensions.md5
import com.example.myapplication.net.AbstractCallback
import com.example.myapplication.net.ServiceApi
import com.example.myapplication.net.bean.request.*
import com.example.myapplication.net.bean.response.BaseResponse
import com.example.myapplication.net.bean.response.InsertFileBean
import com.example.myapplication.ui.FileBean
import com.example.myapplication.ui.upload.FileUploadBean
import com.example.myapplication.ui.upload.FileUploadDao
import com.example.myapplication.utils.Logs
import okhttp3.MediaType
import okhttp3.MultipartBody
import java.io.File
import java.io.FileInputStream
import java.nio.ByteBuffer
import java.util.*
import java.util.concurrent.Callable

class UploadViewModel : FileViewModel<FileUploadBean>() {

    private val dao: UploadDao

    private val remote: ServiceApi

    private var timer: Timer

    private val timerReTask = object : TimerTask() {
        override fun run() {
            if (currTask() >= 2) return
            findReadyTask()?.also {
                Logs.e("UploadViewModel", "Timer > ${it.name}")
                retry(it)
            }
        }
    }

    init {
        val app = App.instance
        dao = app.database.uploadDao()
        remote = app.apiNetwork.remote(ServiceApi::class.java)
        timer = Timer()
        timer.schedule(timerReTask, 500, 2000)
    }


    override fun retry(t: FileUploadBean) {

    }

    // NET

    private fun requestUploadId(t: FileUploadBean) {
        NET.execute {
            try {
                val baseRequest = BaseRequest<EmptyParams>().apply {
                    this.projectId = 565
                    this.userId = App.instance.getUserId()
                    this.token = App.instance.getToken()
                }
                val execute = remote.initUpload(baseRequest).execute()
                if (execute == null || !execute.isSuccessful) {
                    throw FileUploadTaskException("获取文件上传ID请求失败")
                }
                val data = execute.body()?.data ?: throw FileUploadTaskException("获取文件上传ID为空")
                t.apply {
                    this.uploadId = data
                    updateBeanUploadId(t.uuid, data)// 更新上传任务Id
                }
                requestUploadPacket(remote, baseRequest, t)
            } catch (e: Exception) {
                Log.e("FileUploadViewModel", "requestFileUploadId ${e.localizedMessage}")
                updateBeanOther(
                    t.uuid,
                    t.uploadSize,
                    FileUploadBean.UPLOAD_ERROR,
                    0,
                    "${FileBean.STATE_NET_FLAG}网络异常，请点击重新上传"
                )
            }
        }
    }

    private fun requestUploadPacket(
        remote: ServiceApi,
        request: BaseRequest<*>,
        bean: FileUploadBean
    ) {
        // 判断任务是否已满
        addTask {
            Log.e("startUploadFile", "1")
            NET.execute {
                Log.e("startUploadFile", "2")
                val file: File
                try {
                    if (bean.path == null) {
                        throw FileUploadTaskNotExistException("找不到源文件:路径为空")
                    }
                    file = File(bean.path!!)
                    if (!file.exists()) {
                        throw FileUploadTaskNotExistException("找不到源文件:文件不存在")
                    }
                } catch (e: Exception) {
                    updateBeanOther(
                        bean.uuid,
                        bean.uploadSize,
                        FileUploadBean.UPLOAD_ERROR,
                        0,
                        "${FileBean.STATE_LOCAL_FLAG}${e.localizedMessage}"
                    )
                    // 任务标志 -1
                    removeTask()
                    Log.e("startUploadFile", "21 $e")
                    return@execute
                }
                Log.e("startUploadFile", "3")
                val inputStream = FileInputStream(file)
                val channel = inputStream.channel
                val buffer = ByteBuffer.allocate(DEFAULT_CHUNK_SIZE)
                try {
                    var readSize: Int
                    val fileLength = file.length()
                    var uploadSize = bean.uploadSize
                    channel.position(uploadSize)
                    updateBeanOther(
                        bean.uuid,
                        uploadSize,
                        FileUploadBean.UPLOAD_RUNNING, 0,
                        "${FileBean.STATE_DEFAULT_FLAG}已上传:${(uploadSize.toDouble() / fileLength * 100).toInt()}%"
                    )
                    var startTime = System.currentTimeMillis()
                    Log.e("startUploadFile", "4")
                    while (channel.read(buffer).also { readSize = it } > 0) {
                        Log.e("startUploadFile", "41  $readSize")
                        val blockArray = ByteArray(readSize)
                        buffer.flip()
                        buffer.get(blockArray)
                        val checkParam = CheckChunkExistParams(
                            docName = file.name,
                            docSize = fileLength,
                            docChunkNumber = Math.ceil(fileLength.toDouble() / DEFAULT_CHUNK_SIZE)
                                .toInt(),
                            index = (if (uploadSize % DEFAULT_CHUNK_SIZE == 0L) uploadSize / DEFAULT_CHUNK_SIZE else (uploadSize / DEFAULT_CHUNK_SIZE) + 1).toInt(),
                            blockSize = DEFAULT_CHUNK_SIZE,
                            chunkSize = readSize,
                            uploadId = bean.uploadId!!,
                            hashCode = blockArray.md5().toLowerCase(Locale.getDefault())
                        )
                        val uploadCheck =
                            remote.checkChunkExist(BaseRequest<CheckChunkExistParams>(request).apply {
                                this.data = checkParam
                            }).execute()
                        if (uploadCheck.isSuccessful) {
                            val body = uploadCheck.body()
                            if (body.data == false) {
                                val execute = remote.chunkUpload(
                                    hashCode = checkParam.hashCode,
                                    index = checkParam.index,
                                    docChunkNumber = checkParam.docChunkNumber,
                                    uploadId = checkParam.uploadId,
                                    docSize = checkParam.docSize,
                                    chunkSize = checkParam.chunkSize,
                                    blockSize = checkParam.blockSize,
                                    docName = checkParam.docName,
                                    token = App.instance.getToken()!!,
                                    userId = App.instance.getUserId(),
                                    file = MultipartBody.Part.createFormData(
                                        "file",
                                        checkParam.docName,
                                        MultipartBody.create(
                                            MediaType.parse("multipart/form-data"),
                                            blockArray
                                        )
                                    )
                                ).execute()
                                execute.also {
                                    uploadSize += readSize
                                }
                            } else {
                                uploadSize += readSize
                            }
                            val currTime = System.currentTimeMillis()
                            val spaceSecond = (currTime - startTime).toDouble() / 1000
                            val uploadByteSize = readSize / 1024
                            val speed = uploadByteSize / spaceSecond
                            startTime = currTime
                            findSingleTask(bean.uuid)?.also {
                                updateBeanOther(
                                    bean.uuid,
                                    uploadSize,
                                    FileUploadBean.UPLOAD_RUNNING,
                                    speed.toLong(),
                                    "${FileBean.STATE_DEFAULT_FLAG}已上传:${(uploadSize.toDouble() / fileLength * 100).toInt()}%"
                                )
                            } ?: let {
                                throw FileUploadTaskNotFoundException()
                            }
                        }
                        buffer.rewind()
                    }
                    Log.e("startUploadFile", "5")
                    if (uploadSize != fileLength) {
                        throw FileUploadTaskByteErrorException("上传字节不等于文件字节长度")
                    }
                    Log.e("startUploadFile", "6")
                    val insertFileParams = InsertFileParams(
                        docSize = fileLength,
                        docName = file.name,
                        docHashcode = bean.uploadId!!,
                        docVersionRfs = bean.uploadId!!,
                        createBy = App.instance.getUserId(),
                        updateBy = App.instance.getUserId(),
                        projId = 565,
                        docSource = 0,
                        parentId = 412069,
                        docDescript = "上传新文件"
                    )
                    Log.e("startUploadFile", "7")

                    val insertFileRequest = BaseRequest<InsertFileParams>(request).apply {
                        this.data = insertFileParams
                    }
                    Log.e("startUploadFile", "8")

                    remote.insertFile(insertFileRequest)
                        .enqueue(object :
                            AbstractCallback<BaseResponse<InsertFileBean>>("app/rfs/insert") {
                            override fun onSuccess(t: BaseResponse<InsertFileBean>) {
                                val data = t.data ?: return
                                val insertFileInDirParams = InsertFileInDirParams(
                                    projectId = insertFileParams.projId,
                                    docId = data.docId,
                                    docType = "0",
                                    docName = data.docName!!,
                                    rfsId = data.rfsId!!,
                                    auditProjectId = "1659",
                                    parentId = 412069
                                )
                                remote.insertDirFile(BaseRequest<InsertFileInDirParams>(request).apply {
                                    this.data = insertFileInDirParams
                                }).enqueue(object :
                                    AbstractCallback<BaseResponse<Any>>("app/project/doc/insert") {
                                    override fun onSuccess(t: BaseResponse<Any>) {
                                        removeTask()
                                        updateBeanOther(
                                            bean.uuid, uploadSize, FileUploadBean.UPLOAD_FINISH, 0,
                                            "${FileBean.STATE_DEFAULT_FLAG}上传到:${data.docName!!}"
                                        )
                                    }
                                })
                            }
                        })
                } catch (e: Throwable) {
                    e.printStackTrace()
                    removeTask()
                    Log.e("startUploadFile", "10  ${e.localizedMessage} \n $e")
                    if (e is FileUploadTaskByteErrorException) {
                        updateBeanOther(
                            bean.uuid, bean.uploadSize, FileUploadBean.UPLOAD_ERROR, 0,
                            "${FileBean.STATE_NET_FLAG}网络异常，请点击重新上传"
                        )
                    }
                } finally {
                    channel.close()
                    inputStream.close()
                }
            }
        }
    }

    // DAO

    private fun updateAndGetBean(uuid: String, uploadId: String): FileUploadBean? {
        return null
    }

    private fun updateBeanUploadId(uuid: String, uploadId: String) {
        DB.execute {
            dao.update_uploadId_UpdateAt(uuid, uploadId)
        }
    }

    private fun updateBeanOther(
        uuid: String,
        uploadSize: Long,
        uploadState: Byte,
        uploadSpeed: Long,
        state: String
    ) {
        DB.execute {
           // dao.update(uuid, uploadSize, uploadState, uploadSpeed, state)
        }
    }

    override fun insert(t: FileUploadBean) {
        DB.execute {
            dao.insert(t)
            requestUploadId(t)
        }
    }

    override fun delete(t: FileUploadBean) {
        // DB.execute { dao.deleteByUUID(t.uuid) }
    }

    override fun deleteAllFinish() {
        //  DB.execute { dao.deleteFinishAll() }
    }

    override fun listData(): LiveData<List<FileUploadBean>> {
        // return dao.getAlphabetizedWordsByFlow().asLiveData()
        return object : LiveData<List<FileUploadBean>>(){}
    }

    override fun initData() {
        DB.execute {
            dao.insert(FileUploadBean().apply {
                this.name = "进行中"
                this.uploadState = FileUploadBean.UPLOAD_RUNNING
                this.type = 0
                this.enabled = false
            })
            dao.insert(FileUploadBean().apply {
                this.name = "已完成"
                this.uploadState = FileUploadBean.UPLOAD_FINISH
                this.type = 0
                this.enabled = false
            })
        }
    }

    override fun clearAllRunning() {
        DB.execute {
            //  dao.deleteRunningAll()
        }
    }

    override fun clearAllFailure() {
        // DB.execute { dao.deleteErrorAll() }
    }

    override fun updateAllFailure() {
        // DB.execute { dao.updateErrorAll() }
    }

    override fun findReadyTask(): FileUploadBean? {
        // return dao.getOtherNotUploadTask()
        return null
    }

    private fun findSingleTask(uuid: String): FileUploadBean? {
        return dao.find_UUID(uuid)
    }

}